package com.xyzwps.paimon.furina.modules.post

import com.xyzwps.paimon.furina.common.AppErrorCode.*
import com.xyzwps.paimon.furina.common.AppException
import com.xyzwps.paimon.furina.infra.keyBy
import com.xyzwps.paimon.furina.modules.post.entity.Post
import com.xyzwps.paimon.furina.modules.post.entity.PostCollectionRepository
import com.xyzwps.paimon.furina.modules.post.service.PostService
import com.xyzwps.paimon.furina.modules.userinfo.service.UserInfoService
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.transactions.transaction
import org.springframework.stereotype.Service

@Service
class PostApplication(
    private val postService: PostService,
    private val userInfoService: UserInfoService,
    private val collectionRepository: PostCollectionRepository,
    private val db: Database
) {

    fun findById(id: Long, currentUserId: Long): PostDTO = transaction(db) {
        val post = postService.findById(id, currentUserId) ?: throw AppException(POST_NOT_FOUND)
        makePostDTO(post, updatable = true)
    }

    fun findById(id: Long): PostDTO = transaction(db) {
        val post = postService.findById(id) ?: throw AppException(POST_NOT_FOUND)
        makePostDTO(post, updatable = false)
    }

    fun findByCollectionId(id: Long, currentUserId: Long): List<PostDTO> = transaction(db) {
        val posts = postService.findByCollectionId(id, currentUserId)
        makePostDTOList(posts, updatable = true)
    }

    fun findByCollectionId(id: Long): List<PostDTO> = transaction(db) {
        val posts = postService.findByCollectionId(id)
        makePostDTOList(posts, updatable = false)
    }

    fun deleteById(id: Long, currentUserId: Long) = transaction(db) {
        postService.delete(id, currentUserId)
    }

    fun update(id: Long, currentUserId: Long, payload: PostUpdatePayload): PostDTO = transaction(db) {
        val post = postService.update(
            id, currentUserId,
            title = payload.title,
            type = payload.type,
            content = payload.content,
            tagIds = payload.tagIds,
        )
        makePostDTO(post, updatable = true)
    }

    fun create(currentUserId: Long, payload: PostCreatePayload): PostDTO = transaction(db) {
        val post = postService.create(
            currentUserId,
            collectionId = payload.collectionId,
            title = payload.title,
            type = payload.type,
            content = payload.content,
            tagIds = payload.tagIds,
            dedupKey = payload.dedupKey,
        )
        makePostDTO(post, updatable = true)
    }

    private fun makePostDTO(post: Post, updatable: Boolean): PostDTO {
        val author = userInfoService.findSummaryById(post.userId)
        val collection = collectionRepository.findById(post.collectionId)?.let { PostCollectionDTO(it, updatable) }
        return PostDTO(post, updatable = updatable, author = author, collection = collection)
    }

    private fun makePostDTOList(posts: List<Post>, updatable: Boolean): List<PostDTO> {
        if (posts.isEmpty()) return emptyList()

        val authorIds = posts.map { it.userId }.toSet()
        val idToAuthor = userInfoService.findIdToSummaryByIds(authorIds)

        val collectionIds = posts.map { it.collectionId }.toSet()
        val collections = collectionRepository.findByIds(collectionIds)
            .map { PostCollectionDTO(it, updatable) }
        val idToCollection = collections.keyBy { it.id }

        return posts.map {
            PostDTO(
                it,
                updatable = updatable,
                author = idToAuthor[it.userId],
                collection = idToCollection[it.collectionId]
            )
        }
    }
}